module Env where

import qualified Data.Map as Map
import Data.List
import AbsC
import PrintC

type Env = [(Sig, Context)]
type Sig = Map.Map FunIdent FunInfo
type Context = Map.Map Id VarInfo

initDefVars :: DefVars
initDefVars = []

insertId :: Id -> DefVars -> DefVars
insertId i = (i:)

unionDefs :: DefVars -> DefVars -> DefVars
unionDefs d d' = union d d'

initOffset :: PCOffset
initOffset = 0

updateOffset :: PCOffset -> Type -> PCOffset
updateOffset o t  = o + typeWidth t

varDeclType (DVar t _ _ _) = t
varDeclType _ = TErr "This is not a variable declaration.\n"

formParam :: DefVars -> [Parameter] -> DefVars
formParam ds = foldr f ds
	where
	   f (Param _ _ i) d = insertId i d

offsetPars :: [Parameter] -> PCOffset
offsetPars = foldr f initOffset
	where
	   f (Param _ t _) o = updateOffset o t
   
funInfo :: [Parameter] -> Type -> FunInfo
funInfo ps t = FI ps t $ length ps

funIdent :: Id -> [a] -> FunIdent
funIdent i ps = Fun i $ length ps

parVInfo :: Parameter -> PCOffset -> VarInfo
parVInfo (Param m t _) o = VI t o (Just m)

varInfo :: Type -> PCOffset -> VarInfo
varInfo t p = VI t p Nothing

vType :: String -> VarInfo -> Type
vType _ (VI t _ _ ) = t
vType pos (VErr s) = TErr $ pos++s

fType :: FunInfo -> Type
fType (FI _ t _) = t
fType (FErr s) = TErr s

removeDefs :: Env -> DefVars -> DefVars
removeDefs (x:_) xs = remove es xs
	where
		es = Map.keys $ snd x
		remove s n = foldr delete (nub n) s
      
insertLId ((Deref r),_) d = insertRId r d
insertLId ((ArrayEl l r),_) d = insertLId l d
insertLId ((SId i _),_) d = insertId i d

insertRId ((LExpr l),_) d = insertLId l d
insertRId ((Ref l),_) d = insertLId l d
insertRId ((PrePostIncDecr _ l),_) d = insertLId l d
insertRId _ d = d

emptyEnv:: Env
emptyEnv = [(Map.empty, Map.empty)]

newEnv:: Env -> Env
newEnv env = (Map.empty, Map.empty) : env

lookVar :: Env -> Id -> Maybe VarInfo
lookVar [] _ = Nothing
lookVar (y:ys) i = Map.lookup i $ snd y

lookFun :: Env -> FunIdent -> Maybe FunInfo
lookFun [] _ = Nothing
lookFun (y:ys) fi = Map.lookup fi $ fst y

lookupVar:: Env -> Id -> VarInfo
lookupVar [] i = VErr $ " variable \"" ++ printTree i ++ "\" is not declared.\n"
lookupVar (y:ys) i = case Map.lookup i $ snd y of
 		Nothing -> lookupVar ys i
		Just v -> v
      
lookupFun:: Env -> FunIdent -> FunInfo
lookupFun [] fi = FErr $ " function \"" ++ printTree fi ++ "\" is not declared.\n"
lookupFun (y:ys) fi = case Map.lookup fi $ fst y of
		Nothing -> lookupFun ys fi
		Just f -> f

addVar:: Env -> Id -> VarInfo -> Env
addVar e@(y:ys) i vi = case Map.lookup i $ snd y of
 		         Nothing -> (fst y, Map.insert i vi (snd y)) : ys
		         Just _ -> e

addFun:: Env -> FunIdent -> FunInfo -> Env
addFun e@(y:ys) i fi = case Map.lookup i $ fst y of
		Nothing -> (Map.insert i fi (fst y), snd y) : ys
		Just _ -> e 

addPars :: Env -> [Parameter] -> Env
addPars e = snd . foldr f (oI,ne)
	where
	   oI = initOffset
	   ne = newEnv e
	   f p@(Param m t i) (o,n) = (on, addVar n i vi)
	     where
		on = updateOffset o t
		vi = parVInfo p on

parId :: Parameter -> Id
parId (Param _ _ i) = i
